#include <dos.h>
#include <stdio.h>
#include <stdlib.h>

unsigned long GDT_Table[] = {
	0x00000000L,0x00000000L,	//NULL - 00H
	0x0000FFFFL,0x00CF9A00L,	//Code32 _ 08h Base=0 Limit=4G_1 Size=4G
	0x0000FFFFL,0x00CF9200L, 	//Data32 _ 10h Base=0 Limit=4G_1 Size=4G
	0x0000FFFFL,0x00009200L,
   0x0000FFFFL,0x00009800L
};

unsigned char pdescr_tmp[6] = {0}; 	//NULL The IDTR  s Limit=0 CPU will

static unsigned char OldIDT[6] = {0};		//Save The IDTR before Enter Protect Mode.

// disable all Interrupts, include NMI.
#define KeyWait() {while(inportb(0x64) &2);}

void A20Enable(void)
{
	KeyWait ();
	outportb(0x64,0xD1);
	KeyWait();
	outportb(0x60,0xDF); //Enable A20 with 8042.
	KeyWait();
	outportb(0x64,0xFF);
	KeyWait ();
}

void LoadFSLimit4G(void)
{
	A20Enable (); //Enable A20
	//*** Disable ints & Null IDT
	//***
	asm {
			CLI 			//Disable inerrupts
			db 0x0F, 0x01, 0x0E
			dw offset OldIDT
						//SIDT OldIDT		//Save OLD IDTR
			db 0x0F, 0x01, 0x1E
			dw	offset pdescr_tmp
						//LIDT pdescr_tmp
						//Set up empty IDT.Disable any interrupts,
	} // Include NMI.
	//*** Lodd GDTR
	//***
	asm{	// The right Code is Real, since BC++ Linker NOT
			// Work with 32bits Code.
			db 0x66 	//32 bit Operation Prefix in 16 Bit DOS.
			MOV CX,DS 	//MOV ECX,DS
			db 0x66 	//Get Data segment physical Address
			SHL CX,4 	//SHL ECX,4
			MOV word ptr pdescr_tmp [0],(3*8-1)
			//MOV word ptr pdescr_tmp [0], (3*8-1)
			db 0x66
			XOR AX,AX 	//XOR EAX,EAX
			MOV AX,offset GDT_Table
			// MOV AX,offset GDT_Table
			db 0x66
			ADD AX,CX 	//ADD EAX,ECX
			MOV word ptr pdescr_tmp [2], AX
			//GDTR Base low16 bits
			db 0x66
			SHR AX,16 	//SHR EAX,16
			MOV word ptr pdescr_tmp [4],AX
			//GDTR Base high16 bits
			db 0x0F, 0x01, 0x16
			dw	offset pdescr_tmp
							//LGDT pdescr_tmp	//Load GDTR
	}
		//****
		//* Enter 32 bit Flat Protected Mode
		//****
	asm{
			mov DX,0x10 	// The Data32 Selector
			db 0x66,0x0F,0x20,0xC0 	// MOV EAX,CR0
			db 0x66
			MOV BX,AX 	// MOV EBX,EAX
			OR AX,1
			db 0x66,0x0F,0x22,0xC0
			//MOV CRO,EAX -- Set Protection enable bit
			JMP Flush
	} 			//Clear machine perform cache.
	Flush: 		// Now In Flat Mode, But The CS is Real Mode Value.
	asm {	  		//And it  s attrib is 16Bit Code Segment.
			db 0x66
			MOV AX,BX 		//MOV EAX,EBX
			db 0x8E,0xE2 	//MOV FS,DX
			//Load FS Base=0 Size=4G now
			db 0x66,0x0F,0x22,0xC0 	//MOV CRO,EAX
			//Return Real Mode.
			db 0x0F, 0x01, 0x1E
			dw	offset OldIDT
					//LIDT OldIDT 	//LIDT OldIDT -- Restore IDTR
			STI 	// STI -- Enable INTR
	}
}

static int InProtectMode(void)
{
	_asm db 0x66,0x0F,0x20,0xC0 	// MOV EAX,CR0
	return (_AX & 1);
}

int EnterDosFlatMode(void)
{
	if (InProtectMode())
	{
		printf("You are running viskrnl in the DOSBox of Windows/Linux.\n");
		printf("Please boot a pure DOS and try again.\n\n");
		printf("Load failure.\n");
		return 1;
	}
	LoadFSLimit4G();
	return 0;
}

